-- ======================================================================
-- Hell Logic Sniffer Addon 
-- 
-- Copyright 2013 Hell-Prototypes
--
-- http://www.hellprototypes.com/
-- ======================================================================

ModuleClass = "LS_DECODER"
ModuleName  = "RS232"
ModuleTip   = "To decode rs232 data"

RS232_PARITY_MODE_NONE   = 0
RS232_PARITY_MODE_ODD    = 1
RS232_PARITY_MODE_EVEN   = 2
RS232_PARITY_MODE_SPACE  = 3

RS232_STOP_BIT_WIDTH_1   = 0
RS232_STOP_BIT_WIDTH_1_5 = 1
RS232_STOP_BIT_WIDTH_2   = 2

g_byte_mask = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}

g_ch_negedge_table = {}
g_ch_negedge_table_index = 1  --table's key start at 1 
g_start_bit_index = 0
g_channel_bit_len = 0
g_etu_bit_len = 0
g_ch_num = 0

function main ()
    LS_API.DebugMsg("===RS232_Decode.lua main function called===")
    -- param check

    if type(LS_PARAM) ~= "table" then
    	LS_API.MessageBox("<RS232> LS_PARAM was not a table class")
		return
    end
    if #LS_PARAM ~= 5 then
    	LS_API.MessageBox("<RS232> LS_PARAM's size must = 5, size = " .. #LS_PARAM)
    end

    -- get rs232 param
    g_ch_num = LS_PARAM[1]
    g_data_width = LS_PARAM[2]
    g_parity_mode = LS_PARAM[3]
    g_stop_bit_width = LS_PARAM[4]
    g_baud_rate = LS_PARAM[5]
    if g_baud_rate <= 0 then
		LS_API.MessageBox("<RS232> Baud rate was <=0")
		return
	end
    LS_API.DebugMsg("RS232 : " .. g_ch_num .. "," .. g_data_width .. "," .. g_parity_mode  .. "," .. g_stop_bit_width .. "," .. g_baud_rate)
	
	-- get sampling period from common_param api
	common_param = LS_API.GetCommonParameter()
	LS_API.DebugMsg("common_param len = ".. #common_param)
	if #common_param > 0 then
		g_sampling_period = common_param[1]
		if g_sampling_period <= 0 then
			LS_API.MessageBox("<RS232> Sampling Period was <=0")
			return
		end
	end
	LS_API.DebugMsg("sampling_period = ".. g_sampling_period)

	--get channel data
	g_ch_negedge_table = LS_API.GetChannelNegedgeTable(g_ch_num)
	if type(g_ch_negedge_table) ~= "table" then
    	LS_API.MessageBox("<RS232>Read channel data fail...")
		return
    end

    if #g_ch_negedge_table == 0 then
    	LS_API.MessageBox("<RS232>No data in current channel ...")
		return
    end
    LS_API.DebugMsg("g_ch_negedge_table = ".. #g_ch_negedge_table)

    g_channel_bit_len = g_ch_negedge_table[#g_ch_negedge_table]
    LS_API.DebugMsg("g_channel_bit_len = ".. g_channel_bit_len)
    
	--g_ch_etu_count = g_baud_rate * ((g_sampling_period * g_channel_bit_len)/1000000000)
	--LS_API.DebugMsg("g_ch_etu_count = ".. g_ch_etu_count)

	g_etu_bit_len = math.floor((1000000000/g_baud_rate)/g_sampling_period)
	LS_API.DebugMsg("g_etu_bit_len = ".. g_etu_bit_len)

	g_byte_bit_len = g_etu_bit_len * g_data_width
	if g_parity_mode ~= RS232_PARITY_MODE_NONE then
		g_byte_bit_len = g_byte_bit_len + g_etu_bit_len
	end

	
	--find first valid byte
	g_start_bit_index = find_next_failling_edge(0) + math.floor(g_etu_bit_len/2)

	if g_ch_negedge_table_index > #g_ch_negedge_table then
		-- no edge
		LS_API.MessageBox("<RS232>No valid RS232 byte found...")
		return
	end	

  	g_decode_out = {}
    while g_ch_negedge_table_index <= #g_ch_negedge_table do
    	g_bit_index = g_start_bit_index + g_etu_bit_len
    	g_byte_data = 0
    	g_check = 0
  
    	g_decode_out[1] = g_start_bit_index - math.floor(g_etu_bit_len/2)
    	g_decode_out[2] = -1 -- err flag
    	g_byte_sampling_index = 3
    	
    	g_parity_bit_value = false
    	-- check data bit
    	for i=1,g_data_width do
    		g_decode_out[g_byte_sampling_index] = g_bit_index
    		g_byte_sampling_index = g_byte_sampling_index + 1
    		if decode_get_bit_level(g_bit_index) then
    			g_byte_data = g_byte_data + g_byte_mask[i]
    			g_check = g_check + 1
    		end
    		g_bit_index = g_bit_index + g_etu_bit_len
    	end
    	--parity bit
    	if g_parity_mode ~= RS232_PARITY_MODE_NONE then
			g_decode_out[g_byte_sampling_index] = g_bit_index
    		g_byte_sampling_index = g_byte_sampling_index + 1
    		
    		g_parity_bit_value = decode_get_bit_level(g_bit_index)
    		g_bit_index = g_bit_index + g_etu_bit_len
    		
    		if g_parity_mode == RS232_PARITY_MODE_ODD then
    			if (bittest(g_check, 0x01) == g_parity_bit_value) then
    				g_byte_data = -1; --Parity error flag
    			end
    		elseif g_parity_mode == RS232_PARITY_MODE_EVEN then
    			if (bittest(g_check, 0x01) ~= g_parity_bit_value) then
    				g_byte_data = -1; --Parity error flag
    			end
    		end
		end
		--stop bit
		g_decode_out[g_byte_sampling_index] = g_bit_index
    	g_byte_sampling_index = g_byte_sampling_index + 1
    	if not decode_get_bit_level(g_bit_index) then
    		g_byte_data = -2; --Stop bit error flag
    	end

    	--save byte data
   		g_decode_out[2] = g_byte_data

    	PushBack_ret = LS_API.DecodeElementPushBack(g_decode_out)
    	if PushBack_ret ~= 0 then
    		LS_API.MessageBox("<RS232> Fail to push decode result to host... ret =" .. PushBack_ret)
    		return
    	else
    		--LS_API.DebugMsg("=== g_byte_data = ".. g_byte_data)
    	end

    	g_decode_out = {}
		--find next valid byte
		g_start_bit_index = find_next_failling_edge(g_bit_index) + math.floor(g_etu_bit_len/2)
    end
end

function find_next_failling_edge(base)
	local index
    while g_ch_negedge_table_index <= #g_ch_negedge_table do
    	index = g_ch_negedge_table[g_ch_negedge_table_index]
    	if index >= base then
    		return index
    	end
    	g_ch_negedge_table_index = g_ch_negedge_table_index + 1
    end
	return 0
end

function decode_get_bit_level(bit_index)
    local level = LS_API.GetChannelLevelByIndex(g_ch_num, bit_index);
	if level == 1 then
		return true
	else
		return false
    end
end

function bittest(value, mask)
	if (value & mask) > 0 then
		return true
	else
		return false
    end
end